#include "mod.h"

static ProcessManager ProcessManager;
static PIDALLocator pidALLocator;

static PCB* fetchProcess();
static void runFirstProcess();
static PCB* idleProcess;
static void idle();
static Boolean isEmpty();

void InitializeProcessManager() {
    ProcessManager.Current = NULL;
    for(int i=0; i < MAX_PROCESS_COUNT; i++) {
        ProcessManager.RunnableProcesses[i] = ProcessManager.ZombieProcesses[i] = NULL;
    }
    ProcessManager.Front = ProcessManager.Rear = 0;
    CreateKernelProcess(idle);
    idleProcess = fetchProcess();
}

PID AllocatePID() {
    for(i32 i = 0; i < MAX_PROCESS_COUNT / 32; i++) {
        for(i32 j = 0; j< 32; j++) {
            if( (pidALLocator.Bitmap[i] & (1<< j)) ==0 ) {
                pidALLocator.Bitmap[i] |= (1 <<j);
                return i*32 +j;
            }
        }
    }
}

void FreePID(PID pid) {
    pidALLocator.Bitmap[pid /32] &= ~( 1<< (pid %32));//? 这里没看懂
}

PCB* GetCurrentProcess() {
    return ProcessManager.Current;
}

void AddProcess(PCB* process) {
    if ((ProcessManager.Rear + 1) % MAX_PROCESS_COUNT == ProcessManager.Front) {
        Panic("Queue is full");
    }

    ProcessManager.RunnableProcesses[ProcessManager.Rear] = process;
    ProcessManager.Rear = (ProcessManager.Rear + 1) % MAX_PROCESS_COUNT;
}

void AddProcessToZombie(PCB* process) {
    for(i32 i = 0; i < MAX_PROCESS_COUNT; i++) {
        if(ProcessManager.ZombieProcesses[i] == NULL) {
            ProcessManager.ZombieProcesses[i] = process;
            return;
        }
    }

    Panic("The zombie process queue is full");
}

void Schedule() {
    // 如果就绪队列里没有进程，并且当前进程也不存在，说明还未初始化进程管理器，直接退出即可
    if (processManager.Current == NULL && isEmpty()) {
        return;
    }

    // 如果就绪队列里有进程，但是当前进程不存在，说明可以执行第一个进程
    if (processManager.Current == NULL) {
        runFirstProcess();
        return;
    }
    
    // 如果就绪队列里有进程，并且当前进程存在，说明可以执行切换
    PCB* current = processManager.Current;
    if (current->ID && current->Status != PROCESS_STATE_BLOCKED && current->Status != PROCESS_STATE_ZOMBIE) {
        current->Status = PROCESS_STATE_RUNNABLE;
        AddProcess(current);
    }

    if(current->ID && current->Status == PROCESS_STATE_ZOMBIE) {
        AddProcessToZombie(current);
    }

    PCB* next = fetchProcess();
    //下一个进程为用户进程时，需要设置TSS的ESP0为内核栈栈顶，ceil(按页对齐)
    if (next->Type == PROCESS_TYPE_USER) {
        SetTSSEsp0( ( (32)next->KernelStackPointer + PageSize -1)/PageSize * PageSize); 
    }
    //下一个进程的页表和当前进程的页表不同，需要切换页表
    if (next->RootPPN != ProcessManager.Current->RootPPN) {
        SetTRootPageTableAddr(GetAddressFromPPN(next->RootPPN));
    }
    next->Status = PROCESS_STATE_RUNNING;

    ProcessManager.Current = next;
    SwitchProcess(current, next);
}



PCB* TakeZombieProcess(PID pid) {
    PCB* current = ProcessManager.Current;
    for(i32 i=0; i < MAX_PROCESS_COUNT; i++) {
        PCB* process = (PCB*)ProcessManager.ZombieProcesses[i];
        if(process && process->ParentID == current->ID && (process->ID == pid || pid ==-1)) {
            ProcessManager.ZombieProcesses[i] = NULL;
            return process;
        }
    }
    return NULL;
}

    // static methods implement

    static void runFirstProcess() {
        PCB* next = fetchProcess();
        //下一个进程为用户进程时，需要设置TSS的ESP0为内核栈栈顶,ceil按页对齐）
        if(next->Type == PROCESS_TYPE_USER) {
            SetTSSEsp0( ((u32)next->KernelStackPointer + PageSize -1) / PageSize * PageSize);
        }
        // 下一个进程的页表需要上CPU，CR3寄存器
        SetRootPageTableAddr(GetAddressFromPPN(next->RootPPN));
        next->Status = PROCESS_STATE_RUNNING;

        PCB unused;
        PCB* unusedPtr = &unused;
        ProcessManager.Current = next;
        SwitchProcess(unusedPtr, next);
    }
 
    static PCB* fetchProcess() {
        if(isEmpty()) {
            Panic("Queue is empty");
        }

        PCB* result = ProcessManager.RunnableProcesses[ProcessManager.Front];
        ProcessManager.RunnableProcesses[ProcessManager.Front] = NULL;
        ProcessManager.Front = (ProcessManager.Front + 1) % MAX_PROCESS_COUNT;

        return result;
    }

    static void idle() {
        while(TRUE) {
            Schedule();
        }
    }

    static Boolean isEmpty() {
        return ProcessManager.Front == ProcessManager.Rear;
    }


